﻿using System;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.ProjectSystem.Build;
using Microsoft.VisualStudio.ProjectSystem.Utilities;

namespace Red.Scripts.Project.Deploy
{
    [Export( typeof( IDeployProvider ) )]
    [AppliesTo( MyUnconfiguredProject.UniqueCapability )]
    internal class Provider : IDeployProvider
    {
        #region Imports

        /// <summary>
        /// Provides access to the project's properties.
        /// </summary>
        [Import]
        private ProjectProperties Properties { get; set; }

        #endregion Imports

        #region IDeployProvider

        public bool IsDeploySupported => true;

        public async Task DeployAsync( CancellationToken cancellationToken, System.IO.TextWriter outputPaneWriter )
        {
            RedScriptsProjectDeploy deployProperties = await Properties.GetRedScriptsProjectDeployPropertiesAsync();
            
            string skipDeployRaw = await deployProperties.SkipDeploy.GetEvaluatedValueAtEndAsync();

            bool skipDeploy = false;
            if( !bool.TryParse( skipDeployRaw, out skipDeploy ) )
                throw new Exception( "Invalid value specified for SkipDeploy" );

            if ( skipDeploy )
                return;

            string gamedef = await deployProperties.GameDef.GetEvaluatedValueAtEndAsync();

            if ( !string.IsNullOrWhiteSpace( gamedef ) && gamedef != deployProperties.GameDef.DefaultValue )
            {
                Utility.DefinesAccessor defines = new Utility.DefinesAccessor();
                await defines.Load();

                Process process = new Process();

                process.StartInfo.FileName = await deployProperties.RedScriptsPathToCmdlet.GetEvaluatedValueAtEndAsync();
                process.StartInfo.WorkingDirectory = await deployProperties.RedScriptsCmdletCWD.GetEvaluatedValueAtEndAsync();
                process.StartInfo.Arguments = await Utility.CommandlineBuilder.ConstructDeploy( Properties, defines );
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.CreateNoWindow = true;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardError = true;
                process.EnableRaisingEvents = true;

                process.OutputDataReceived += async ( object sender, DataReceivedEventArgs e ) => { await outputPaneWriter.WriteLineAsync( e.Data ); };
                process.ErrorDataReceived += async ( object sender, DataReceivedEventArgs e ) => { await outputPaneWriter.WriteLineAsync( e.Data ); };

                TaskCompletionSource<bool> processFinishedTask = new TaskCompletionSource<bool>();
                process.Exited += ( object sender, EventArgs e ) => { processFinishedTask.SetResult( true ); };

                if ( process.StartInfo.FileName == @"<Automatic>" )
                {
                    process.StartInfo.FileName = defines.Locate( "cmdlet.exe" );
                }

                if ( string.IsNullOrWhiteSpace( process.StartInfo.FileName ) )
                    throw new Exception( "Could not find path to cmdlet.exe" );

                if ( process.StartInfo.WorkingDirectory == @"<ExecutableDirectory>" )
                {
                    process.StartInfo.WorkingDirectory = System.IO.Path.GetDirectoryName( process.StartInfo.FileName );
                }

                if ( process.Start() )
                {
                    await outputPaneWriter.WriteLineAsync( $"{process.StartInfo.FileName} Started with process id: {process.Id}" );

                    process.BeginOutputReadLine();
                    process.BeginErrorReadLine();

                    cancellationToken.Register( () => { processFinishedTask.TrySetCanceled(); } );

                    await processFinishedTask.Task;
                }
            }
        }

        public void Commit()
        {
        }

        public void Rollback()
        {
        }

        #endregion IDeployProvider
    }
}